﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.IO;

namespace eTerm.PlugIn.NCore
{
    public sealed class PlugInContext
    {
        private static readonly PlugInContext __instance = new PlugInContext();

        private Thread searchThread;


        private PlugInContext() { 
            
        }

        /// <summary>
        /// 插件管理器单例实体.
        /// </summary>
        /// <value>The instace.</value>
        public static PlugInContext Instance { get { return __instance; } }


        public event EventHandler<PlugInEventArgs> OnSearchedPlugIn;

        public event EventHandler<PlugInEventArgs> OnCompleted;

        public event EventHandler<PlugInEventArgs> OnNotify;

        public event EventHandler<PlugInEventArgs> OnExecuting;

        public event EventHandler<PlugInEventArgs> OnExecuted;

        public event EventHandler<PlugInEventArgs> OnException;


        /// <summary>
        /// 自动执行插件队列.
        /// </summary>
        private void ExecutePlugIn() {
            lock (this)
            {
                if (__QueuePlugIns.Count == 0)
                {
                    if (OnCompleted != null)
                        OnCompleted(this, new PlugInEventArgs(null));
                    return;
                }
                ExecutePlugIn(__QueuePlugIns.Dequeue());
            }
        }


        #region 插件事件
        /// <summary>
        /// Handles the OnException event of the PlugIn control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="PlugInEventArgs"/> instance containing the event data.</param>
        private void PlugIn_OnException(object sender, PlugInEventArgs e)
        {
            if (OnException != null)
                OnException(e.PlugIn, e);
        }

        /// <summary>
        /// Handles the OnExecuted event of the PlugIn control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="PlugInEventArgs"/> instance containing the event data.</param>
        private void PlugIn_OnExecuted(object sender, PlugInEventArgs e)
        {
            if (OnExecuted != null)
                OnExecuted(e.PlugIn, e);
            ExecutePlugIn();            //执行下一个插件
        }

        /// <summary>
        /// Handles the OnNotify event of the PlugIn control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="PlugInEventArgs"/> instance containing the event data.</param>
        private void PlugIn_OnNotify(object sender, PlugInEventArgs e)
        {
            if (OnNotify != null)
                OnNotify(e.PlugIn, e);
        }

        /// <summary>
        /// Handles the OnExecuting event of the PlugIn control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="PlugInEventArgs"/> instance containing the event data.</param>
        private void PlugIn_OnExecuting(object sender, PlugInEventArgs e)
        {
            if (OnExecuting != null)
                OnExecuting(e.PlugIn, e);
        }

        /// <summary>
        /// Handles the OnFrequency event of the PlugIn control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="PlugInEventArgs"/> instance containing the event data.</param>
        private void PlugIn_OnFrequency(object sender, PlugInEventArgs e)
        {
            __QueuePlugIns.Enqueue(e.PlugIn);
            ExecutePlugIn();
        }


        #endregion

        /// <summary>
        /// 绑定插件事件并执行.
        /// </summary>
        /// <param name="__plugIn">The __plug in.</param>
        private void ExecutePlugIn(IPlugIn plugIn)
        {
            plugIn.OnException -= PlugIn_OnException;
            plugIn.OnException += PlugIn_OnException;

            plugIn.OnExecuted -= PlugIn_OnExecuted;
            plugIn.OnExecuted += PlugIn_OnExecuted;

            plugIn.OnNotify -= PlugIn_OnNotify;
            plugIn.OnNotify += PlugIn_OnNotify;

            plugIn.OnExecuting -= PlugIn_OnExecuting;
            plugIn.OnExecuting += PlugIn_OnExecuting;

            plugIn.OnFrequency -= PlugIn_OnFrequency;
            plugIn.OnFrequency += PlugIn_OnFrequency;

            plugIn.Execute();
        }


        private Queue<IPlugIn> __QueuePlugIns = new Queue<IPlugIn>();
        private List<IPlugIn> plugIns = new List<IPlugIn>();


        /// <summary>
        /// 释放插件资源.
        /// </summary>
        public void Dispose() {
            plugIns.ForEach(l => {
                l.Dispose();
            });
        }

        private string PlugInsDirectory { get; set; }

        public void Start(string plugInsDirectory)
        {
            if (Directory.Exists(Path.Combine(Path.GetDirectoryName(this.GetType().Assembly.Location), plugInsDirectory)))
                this.PlugInsDirectory = Path.Combine(Path.GetDirectoryName(this.GetType().Assembly.Location), plugInsDirectory);
            else
                this.PlugInsDirectory = Path.GetDirectoryName(this.GetType().Assembly.Location);
            searchThread = new Thread(new ThreadStart(Search));
            if (searchThread.ThreadState == System.Threading.ThreadState.Stopped)
            {
                searchThread = null;
                searchThread = new Thread(new ThreadStart(Search));
            }

            if (!searchThread.IsAlive)
                searchThread.Start();
        }



        private void Search()
        {
            plugIns.Clear();
            QueryPlugIn();
            foreach (IPlugIn plugIn in plugIns.OrderBy(l => l.ExecuteOrder)/*.OrderBy(l=>l.GetType().Name).Where(l => l.AutoRun)*/)
            {
                __QueuePlugIns.Enqueue(plugIn);
            }
            ExecutePlugIn();
        }


        private void QueryPlugIn()
        {
            foreach (FileInfo file in new DirectoryInfo(this.PlugInsDirectory).GetFiles(@"*.dll", SearchOption.AllDirectories))
            {
                System.Reflection.Assembly ass = System.Reflection.Assembly.LoadFile(file.FullName);
                try
                {
                    foreach (Type t in ass.GetTypes())
                    {
                        if (t.GetInterface(typeof(IPlugIn).Name, true) != null && !t.IsAbstract)
                        {
                            IPlugIn plugIn = (IPlugIn)ass.CreateInstance(t.FullName, true);
                            if (OnSearchedPlugIn != null)
                                OnSearchedPlugIn(plugIn, new PlugInEventArgs(plugIn));
                            plugIns.Add(plugIn);
                        }
                    }
                }
                catch { }
            }
        }

    }
}
